package com.jiepu.daily.corejava.advanceed.part2.day0717;

import java.util.Arrays;

/**
 * @Description:
 * @author: xinjie.txt
 * @date: 2020/7/20  8:29
 */
public class MyCollectionImpl implements MyCollection{
	// 真正用于保存元素的数组，在构造函数中对其进行初始化
	private Object[] data;

	// 表示默认初始值的常量
	public final static int DEFAULT_CAPACITY = 10;

	// 空数组常量
	private final static Object[] EMPTY_DATA = new Object[] {};

	// 当前已经插入元素的数量，也代表新元素应该保存的索引位置
	private int size = 0;

	/*
	 * 给用户提供了两种初始化逻辑：
	 * 1、如果用户没有传参，我们给出默认的数组长度
	 * 2、如果用户传了参数，就使用该参数作为长度初始化数组
	 */
	public MyCollectionImpl() {
		this(0);
	}
	public MyCollectionImpl(int capacity) {
		if (capacity > 0) {
			data = new Object[capacity];
		} else if (capacity == 0)
			data = EMPTY_DATA;
		else {
			throw new IllegalArgumentException("Illegal Capacity: "
					+ capacity);
		}
	}

	@Override
	public Looper looper() { // 返回一个新的遍历器
		return new MyLooper();
	}

	// 使用内部类实现集合的遍历
	private class MyLooper implements Looper { // 遍历器
		// position记录当前遍历器对象进行到的位置
		private int position;
		// 判断是否存在下一个元素
		@Override
		public boolean hasNext() {
			return position < size;
		}
		// 给用户提供一个方法用于访问集合中的元素
		@Override
		public Object next() {
			// 每次返回数组中的一个元素
			return data[position++];
		}
	}



	@Override
	public boolean add(Object o) {
		// 先调用方法确保容量够用
		ensureCapacity();
		// 把o插入到data数组中
		data[size++] = o;
		return true;
	}

	// 用来确保数组容量够用
	private void ensureCapacity() { // 扩容数组
		if (size == data.length) {

			// 计算要给数组重新赋值的长度是多少
			// 如果是初始化扩容，用默认的初始长度
			// 否则，长度扩充一倍
			int newCapacity = data.length == 0 ?
					DEFAULT_CAPACITY : data.length + data.length >> 1;

			// 先创建一个比之前的数组更大的新数组
			Object[] newData = new Object[newCapacity];

			// 把原数组中的所有元素复制到新数组的对应位置
			System.arraycopy(data, 0, newData, 0, data.length);

			// 让新数组充当data
			data = newData;
		}
	}

	// 清空集合中所有的元素
	@Override
	public void clear() {
		// 所谓的清空指的就是集合对象不再保存所有元素对象的引用
		// 方法一：在原数组基础上做操作，遍历并且把每个保存的对象引用都清除
		// 优点是不需要占用额外的内存，缺点是消耗更多时间做数组遍历
		for (int i = 0; i < size; i++) {
			data[i] = null;
		}

		// 方法二：直接把data指向一个新创建的数组，丢弃原有的数组
		// 优点是时间效率比较高，缺点是需要额外开辟空间创建新的数组
		// data = new Object[data.length];

		// 把size归零
		size = 0;
	}

	// 判断集合中是否包含指定元素
	@Override
	public boolean contains(Object o) {
		// 思路：遍历整个集合，把o和每个元素进行比较
		// 如果能够找到一个相同的，就说明集合中包含该元素，返回true
		// 如果遍历完毕之后还没有找到任何一个和o相同的元素，返回false
		// 如果传入的o为空，通过==null找元素
		// 如果传入的o不为空，通过equals()方法找元素
		if (o == null) {
			for (int i = 0; i < size; i++) {
				if (data[i] == null)
					return true;
			}
		} else {
			for (int i = 0; i < size; i++) {
				if (o.equals(data[i])) {
					return true;
				}
			}
		}
		return false;
	}

	@Override
	public boolean isEmpty() {
		return size == 0;
	}

	// 从集合中删除指定元素
	@Override
	public boolean remove(Object o) {
		// 从集合中删除指定元素
		// 因为传入的参数就要被删除的元素，并不是直接指定要删第几个
		// 所以第一步要现在集合中找这个元素
		if (o == null) {
			for (int i = 0; i < size; i++) {
				if (data[i] == null) {
					fastRemove(i);
					return true;
				}
			}
		} else {
			for (int i = 0; i < size; i++) {
				if (o.equals(data[i])) {
					fastRemove(i);
					return true;
				}
			}
		}
		return false;
	}

	private void fastRemove(int index) {
		// 把[i]右边的所有元素全部左移
		// 实现数组中批量相邻元素的位移有不止一种方法：
		// 1、循环实现
		// 2、数组复制实现
		/*for (int j = i; j < size - 1; j++) {
			data[j] = data[j + 1];
		}*/

		// 计算要位移的元素有多少（数组复制的 长度）
		int numMoved = size - index - 1;
		System.arraycopy(data, index + 1, data, index, numMoved);
		data[--size] = null;
	}

	// 返回集合中的元素个数
	@Override
	public int size() {
		return size;
	}

	// 临时定义一个方法打印输出所有已插入的元素
	// 为了方便在编码的过程中观察功能是否能够实现
/*	public void print() {
		System.out.print("[");
		for (int i = 0; i < size; i++) {
			System.out.print(data[i]);
			if (i != size - 1)
				System.out.print(", ");
		}
		System.out.println("]");
	}*/

	// 返回包含此集合中所有元素的数组（获取副本z）
	@Override
	public Object[] toArray() {
		// 把已经插入到集合中的元素复制一份放在一个新的数组中返回
		// Object[] array = new Object[size];
		// System.arraycopy(data, 0, array, 0, size);
		return Arrays.copyOf(data, size);
	}

}
